Skip to content

Refactor runtime decisions into a functional core and fix wake/timeout edge cases - #109

Merged
egorsmkv merged 1 commit into
masterfrom
refactor/functional-core
Oct 10, 2026
Merged

egorsmkv merged 1 commit into
masterfrom
refactor/functional-core

Conversation

@egorsmkv

Copy link
Copy Markdown
Contributor

This follows the repository-wide functional-programming review of master at 42b842a (v0.1.61).

The review found two concrete failure modes: AsyncEvent sends notifications while holding its mutex, so a synchronous waker that registers another listener deadlocks; finite but oversized TLS timeouts reach a panicking Duration conversion. Both were reproduced with failing regression tests before the fixes.

Changes:

  • Snapshot AsyncEvent registrations under the mutex, then notify outside it. Registrations made by callbacks remain pending for the next notification. Recycle the snapshot allocation only when no live registrations would be overwritten.
  • Extract a pure Interval scheduling decision from timer registration and state mutation. Commit the next deadline only after the wait completes, preserving cancellation, zero-period yielding, missed-tick cadence and constant-time catch-up.
  • Separate scalar TLS timeout validation from Python error construction. Preserve defaults, rounding and existing invalid-input messages; report oversized values as ValueError. Reject an overflowing handshake deadline with InvalidInput before handshake I/O.
  • Add regression tests and a small benchmark of the actual notification/timer implementations. Public signatures and dependencies remain unchanged.

Review scope:

  • Inventoried all 280 tracked code/configuration files (85,760 lines at the base), including 169 Rust, 87 Python and 4 shell source/test/tool/example files.
  • Structurally scanned every source file; parsed all Python files and checked shell syntax. Reviewed effect/ownership boundaries across the Python bindings, scheduler/timers, transports/TLS/processes, embedded runtime/drivers/I/O and supporting tools.
  • The deeper changes are deliberately confined to the three areas above. Existing local mutation and I/O loops were retained where rewriting them would add allocation or obscure ownership. This is a repository-wide structural review with targeted deep analysis, not a claim of exhaustive manual verification or proof that other bugs do not exist.

Validation on Linux x86_64, CPython 3.14.7, Rust nightly-2026-09-25:

  • Release extension built successfully.
  • Rust default: 328 passed, 15 failed. All features: 424 passed, 19 failed. Every failure reports EPERM from unavailable io_uring or Unix-socket operations; these full commands are not green in this environment.
  • Python release suite with a local Redis server: 473 passed, 2 failed, 3 skipped, 98 tooling tests deselected. The two failures are Unix-socket creation denied with EPERM. The skips require Winsock, free-threaded Python and a profile extension respectively.
  • Redis/hiredis integration ran for asyncio, uvloop and rsloop. Tooling suite separately: 98 passed.
  • Root and standalone runtime harness Clippy, all targets/all features, warnings denied: passed. Rust formatting, changed Python file Ruff lint/format and diff whitespace checks: passed.
  • Focused Pyright for python/rsloop, tests/typing and tests/test_tls.py: 0 errors. Full-tree Pyright was attempted and reports 64 errors with optional integration dependencies absent; it is not claimed to pass locally.
  • Hotpath coverage: 2079 function definitions, zero missing hooks; all 3 coverage-tool tests passed.
  • New regressions cover synchronous listener re-registration, snapshot/capacity reuse, independent timer-cadence reference cases, cancelled future ticks, defaults/tiny positive timeout rounding, invalid/oversized timeouts and overflowing native deadlines.
  • Windows/macOS, free-threaded Python and a working io_uring runtime were not exercised locally.

Performance:
Identical uninstrumented release builds against 42b842a, pinned to CPU 0, ABBA order. The new functional_core example used 100,000 rounds × 7 samples per run (14 samples/build); the existing stream_read_lifecycle benchmark used 20,000 rounds × 5 samples per run (10 samples/build). Medians, nanoseconds/round:

Case master this branch
Notify 0 listeners 4.48 4.44
Notify 1 listener 77.44 70.33
Notify 4 listeners 258.72 243.89
Notify 16 listeners 1009.81 969.82
Overdue CatchUp tick 41.22 38.76
Buffered read 281.77 284.81
Buffered readexactly 286.83 285.61
Buffered readuntil 327.14 337.02
Waiting read 2450.01 2410.37
Waiting readexactly 1601.80 1598.46
Waiting readuntil 1615.17 1646.31

These are local microbenchmarks, not a production speedup claim. Notification snapshots require a second lock acquisition on nonempty broadcasts to recycle capacity; reentrant registrations may allocate separately. The measured stream differences range from -1.6% to +3.0%.

Reproduce the added benchmark:

cargo run --release --manifest-path tools/vibeio-check/Cargo.toml --example functional_core -- 100000 7

@egorsmkv
egorsmkv merged commit 5f5027c into master Oct 10, 2026
38 checks passed
@egorsmkv
egorsmkv deleted the refactor/functional-core branch October 10, 2026 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant